package com.credithc.eagleeye.plugins.rest;
import com.credithc.eagleeye.agent.factory.EagleEyeAgentFactory;
import com.credithc.eagleeye.agent.util.HostUtil;
import com.credithc.eagleeye.constants.RpcTypeEnum;
import com.credithc.eagleeye.exception.EagleEyeIgnoreException;
import com.credithc.eagleeye.plugins.util.LogUtil;
import com.credithc.eagleeye.thread.EagleEyeThreadContext;
import com.credithc.eagleeye.thread.TraceInfo;
import com.credithc.eagleeye.util.MixUtil;
import com.credithc.eagleeye.util.ThrowableToStringArray;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import static com.credithc.eagleeye.constants.EagleEyeConstant.HC_RPC_ID;
import static com.credithc.eagleeye.constants.EagleEyeConstant.HC_TRACE_ID;
/**
 * 描 述: <br/>
 * 作 者: zhangjin<br/>
 * 创 建：2019年10月28日<br/>
 * 版 本：v1.0.0<br>
 * <p>
 * 历 史: (版本) 作者 时间 注释 <br/>
 */
@Slf4j
public class EagleEyeRestTemplateInterceptor implements ClientHttpRequestInterceptor {
    public EagleEyeRestTemplateInterceptor() {
        log.info("EagleEyeRestTemplateInterceptor init..");
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        log.debug("EagleEyeRestTemplateInterceptor around start...");
        ClientHttpResponse response=null;
        TraceInfo traceInfo = EagleEyeThreadContext.getTraceInfo();
        if (traceInfo == null) {
            traceInfo = new TraceInfo();
            EagleEyeThreadContext.init();
            EagleEyeThreadContext.putTraceInfo(traceInfo);
        }else{
            traceInfo.addSequenceNo();
        }
        injectEagleEyeHeader(request, traceInfo);
        RpcTraceInfoVo rpcTraceInfoVo = initRpcTraceInfoVo(request, traceInfo);
        //执行调用
        long beginTime = System.currentTimeMillis();
        try {
            response = execution.execute(request, body);
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
            if(response.getStatusCode().is4xxClientError()||response.getStatusCode().is5xxServerError()) {
                rpcTraceInfoVo.setResult("ERROR");
            }else{
                rpcTraceInfoVo.setResult("OK");
            }
        } catch (Exception ex) {
            if (!(ex instanceof EagleEyeIgnoreException)) {
                //ERROR
                rpcTraceInfoVo.setResult("ERROR");
                rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
                //异常的结果
                String[] traceStr = ThrowableToStringArray.convert(ex);
                StringBuilder builder = new StringBuilder();
                for(String trace : traceStr) {
                    builder.append(trace);
                    builder.append("\n");
                }
                rpcTraceInfoVo.setResponseJson(builder.toString());
            } else {
                //OK
                rpcTraceInfoVo.setResult("OK");
            }
            throw ex;
        } finally {
            EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
            LogUtil.printSplunkLog(rpcTraceInfoVo);
        }
        log.debug("EagleEyeRestTemplateInterceptor around end...");
        return response;
    }

    private void injectEagleEyeHeader(HttpRequest request, TraceInfo traceInfo) {
        HttpHeaders headers = request.getHeaders();
        if (!headers.containsKey(HC_TRACE_ID)) {
            List<String> traceId = new LinkedList<>();
            traceId.add(traceInfo.getTraceId());
            headers.put(HC_TRACE_ID, traceId);
            List<String> rpcId = new LinkedList<>();
            rpcId.add(traceInfo.getRpcId());
            headers.put(HC_RPC_ID, rpcId);
        }
    }

    private RpcTraceInfoVo initRpcTraceInfoVo(HttpRequest request, TraceInfo traceInfo) {
        HttpMethod method = request.getMethod();

        RpcTraceInfoVo rpcTraceInfoVo = new RpcTraceInfoVo();
        Date date = new Date();
        rpcTraceInfoVo.setRequestDateTime(MixUtil.ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(date));
        rpcTraceInfoVo.setTraceId(traceInfo.getTraceId());
        rpcTraceInfoVo.setRpcId(traceInfo.getRpcId());
        rpcTraceInfoVo.setRpcType(RpcTypeEnum.REST.getRpcName());
        rpcTraceInfoVo.setServiceCategory("restTemplate");
        rpcTraceInfoVo.setServiceName("restTemplate");
        rpcTraceInfoVo.setMethodName(method==null?"":method.toString());
        rpcTraceInfoVo.setClientHost(HostUtil.getIp());
        rpcTraceInfoVo.setServerHost(request.getURI().toString());
        return rpcTraceInfoVo;
    }

}
